<!--
    http://www.liaoxuefeng.com/article/001426512790239f83bfb47b1134b63b09a57548d06e5c5000
-->
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title></title>
</head>

<body>
    <div id="main"></div>
    <div id="main2"></div>
</body>
<script id="tpl" type="text/plain">
    <p>Today: { date }</p>
    <a href="/{ user.id|safe }">{ user.company }</a>
</script>
<script src="../common/jquery.js"></script>
<script>
    /*
            知识点：
            new Function('sssss') 返回什么
            正则
            exec方法
            String方法
            substring
            apply
            while
        */

    function Template(tpl) {
        var
            fn,
            match,
            code = [
                //html转义,把obj中包含的<>转义出来
                'var r=[];\nvar _html = function (str) { return str.replace(/&/g, \'&amp;\').replace(/"/g, \'&quot;\').replace(/\'/g, \'&#39;\').replace(/</g, \'&lt;\').replace(/>/g, \'&gt;\'); };'
            ],

            //用正则来匹配{ xxx.xxx | safe }
            re = /\{\s*([a-zA-Z\.\_0-9()]+)(\s*\|\s*safe)?\s*\}/m,
            addLine = function (text) {
                code.push('r.push(\'' + text.replace(/\'/g, '\\\'').replace(/\n/g, '\\n').replace(/\r/g, '\\r') +
                    '\');');
            };
        while (match = re.exec(tpl)) {

            //如果匹配成功
            if (match.index > 0) {
                addLine(tpl.slice(0, match.index));
            }
            if (match[2]) {
                code.push('r.push(String(this.' + match[1] + '));');
            } else {
                code.push('r.push(_html(String(this.' + match[1] + ')));');
            }
            tpl = tpl.substring(match.index + match[0].length);
        }
        addLine(tpl);
        code.push('return r.join(\'\');');
        //console.log(code.join('\n'));
        fn = new Function(code.join('\n'));
        console.log(fn);
        /*
        可以说编译原理是把对象的值输出了，再拼接的
        不是拼接在一起，再输出对象的值
        (function() {
            var r=[];
            var _html = function (str) { return str.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); };
            r.push('\n    <p>Today: ');
            r.push(_html(String(this.date)));
            r.push('</p>\n    <a href="/');
            r.push(String(this.user.id));
            r.push('">');
            r.push(_html(String(this.user.company)));
            r.push('</a>\n');
            return r.join('');
        })
        */
        this.render = function (model) {
            return fn.apply(model);
        };
    }

    var tpl = new Template($('#tpl').html());
    var s = tpl.render({
        date: 20150101,
        user: {
            id: 'A-000&001',
            company: 'AT&T<a>'
        }
    });
    $('#main').html(s);

    /*
    自己写
    */

    (function () {
        var obj = {
            user: {
                company: '恒拓开源'
            }
        };
        var html = "<div>{user.company}</div>";

        var _template = function (obj) {

            //第一种，也可以，不过不规范
            // var tap = "'<div>'+";
            // var tap2 = "+'</div>'"
            // var str = "user.company";
            // var str = "return " + tap + "obj." + str + tap2;

            //第二种：谨记两层push，代码多的话比较清晰
            var code = [];
            code.push('var r = [];');
            code.push("r.push('<div>');");
            code.push('r.push(obj.user.company);');
            code.push('r.push("</div>");');
            code.push('return r.join("")');
            var str = code.join('');

            var fn = new Function("obj", str);
            return fn(obj);
            /*
            两种方法编译出来的fn：第二种不用“+”
            fn = function(obj){
                return "<div>"+obj.user.company+"</div>";
            }   
            
            fn = function(obj){
                var r=[]; 
                r.push("<div>");
                r.push(obj.user.company);
                r.push("</div>");
                return r.join('');
                }
             */
        }
        $('#main2').html(_template(obj))
    })()
</script>

</html>